﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;
using System.IO;

namespace SqlBlocks
{
    public static class AssemblyHelper
    {
        #region Project file helpers
        /// <summary>
        /// Builds a project using MSBuild and returns the resulting assembly.
        /// </summary>
        /// <param name="projectPath">Path to a project file</param>
        /// <returns>The generated assembly</returns>
        public static Assembly GetProjectAssembly(string projectPath) 
        {
            var path = Build(projectPath);

            if (File.Exists(path))
            {
                return Assembly.LoadFile(path);
            }

            return null;
        }

        /// <summary>
        /// Builds a project using MSBuild and returns the output
        /// </summary>
        /// <param name="path">Path to a project file.</param>
        /// <returns>The path to the generated assembly</returns>
        public static string Build(string projectPath)
        {
            string msbuildLoc = Environment.GetFolderPath(Environment.SpecialFolder.System);
            msbuildLoc = Path.Combine(msbuildLoc, "..\\Microsoft.net\\Framework");
            Version v = Environment.Version;
            msbuildLoc = Path.Combine(msbuildLoc, string.Format("v{0}.{1}.{2}\\msbuild.exe", v.Major, v.Minor, v.Build));

            if (!File.Exists(msbuildLoc))
            {
                throw new FileNotFoundException("Unable to locate MSBuild at " + msbuildLoc);
            }

            ProcessStartInfo pinfo = new ProcessStartInfo(msbuildLoc);
            pinfo.RedirectStandardOutput = true;
            pinfo.Arguments = string.Format("/verbosity:m /nologo \"{0}\"", projectPath);
            pinfo.UseShellExecute = false;
            Process p = Process.Start(pinfo);
            string output = p.StandardOutput.ReadToEnd();
            string[] lines = output.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
            if (lines.Length > 0)
            {
                string assembly = lines[lines.Length - 1];
                return assembly.Trim().Substring(assembly.IndexOf("-> ") + 3);
            }
            return output;
        }
        #endregion

        public static IEnumerable<Type> GetImplementingTypes(this Assembly assembly, Type type)
        {
            foreach (Type t in assembly.GetTypes())
            {
                if (type.IsAssignableFrom(t))
                {
                    yield return t;
                }
            }
        }

        /// <summary>
        /// Gets a list of Migrations to run from the specified assembly
        /// </summary>
        /// <param name="assembly"></param>
        /// <returns></returns>
        public static List<MigrationDescription> GetMigrations(Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly");
            }
            var migrations = new List<MigrationDescription>();
            foreach (Type t in assembly.GetImplementingTypes(typeof(IMigration)))
            {
                var desc = MigrationDescription.BuildFromType(t);
                if (desc != null)
                {
                    migrations.Add(desc);
                }
            }
            migrations.Sort();
            return migrations;
        }
    }
}
